#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

namespace BlockQueueModule
{
    //version 2 ：自己封装的阻塞队列
    using namespace LockModule;
    using namespace CondModule;

    static const int gcap = 10;
    template<typename T>
    class BlockQueue
    {
    private:
        bool IsEmpty()
        {
            return _q.size() == _cap;
        }
        bool IsFull()
        {
            return _q.empty();
        }
    public:
        BlockQueue(int cap = gcap)
            :_cap(cap)
            ,_cwait_num(0)
            ,_pwait_num(0)
        {}
        // 入队列
        void Equeue(const T& in)
        {
            LockGuard lock_guard(_mutex);  // 创建出来即加锁，整个生命周期相当于临界区
            // 不是你想生产就能生产，必须队列不为满的情况下才能入队列，否则需要阻塞等待
            while(IsFull())
            {
                cout << "生产者进入等待..." << endl;
                // 等待时会释放mutex
                _pwait_num++;
                _productor_cond.Wait(_mutex);  // wait的时候，是必定持有锁的；什么时候返回？线程被唤醒&&重新申请并持有锁（会在临界区醒来）
                _pwait_num--;
                cout << "生产者被唤醒了..." << endl;
            }
            // isfull不满足或者线程被唤醒了，就能生产 
            _q.push(in);
            // 走到这里肯定有数据，因为刚push 完，所以如果消费者有wait的线程，就可以唤醒一个
            if(_cwait_num)
            {
                cout << "叫醒消费者..." << endl;
                _consumer_cond.Signal();
            }
            // 不需要特意解锁
        }
        void Pop(T* out)
        {
            LockGuard lock_guard(&_mutex);
            // 不是你想消费就能消费，必须在队列不为空的情况下才能出队列，否则需要阻塞等待
            while(IsEmpty())
            {
                cout << "消费者进入等待..." << endl;
                _cwait_num++;
                _consumer_cond.Wait(&_mutex);
                _cwait_num--;
                cout << "消费者被唤醒了..." << endl;
            }
            // isempty不满足或者线程被唤醒了，就能消费 
            // 走到这里肯定不为满，因为刚pop完，所以如果生产者有wait的线程，就可以唤醒一个
            if(_pwait_num)
            {
                cout << "叫醒生产者..." << endl;
                _productor_cond.Signal();
            }
            *out = _q.front();
            _q.pop();
        }
        ~BlockQueue()
        {}
    private:
        queue<T> _q;  // 保存数据的容器，临界资源
        int _cap;  // blockqueue的最大容量
        Mutex _mutex;  // 互斥锁
        Cond _productor_cond;  // 生产者条件变量
        Cond _consumer_cond;  // 消费者条件变量

        int _cwait_num;  // 保存生产者wait的数量
        int _pwait_num;  // 保存消费者wait的数量
    };



    // version 1 :用库函数写的阻塞队列
    // static const int gcap = 10;
    // template<typename T>
    // class BlockQueue
    // {
    // private:
    //     bool IsEmpty()
    //     {
    //         return _q.size() == _cap;
    //     }
    //     bool IsFull()
    //     {
    //         return _q.empty();
    //     }
    // public:
    //     BlockQueue(int cap = gcap)
    //         :_cap(cap)
    //         ,_cwait_num(0)
    //         ,_pwait_num(0)
    //     {
    //         pthread_mutex_init(&_mutex, nullptr);
    //         pthread_cond_init(&_productor_cond, nullptr);
    //         pthread_cond_init(&_consumer_cond, nullptr);
    //     }
    //     // 入队列
    //     void Equeue(const T& in)
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         // 不是你想生产就能生产，必须队列不为满的情况下才能入队列，否则需要阻塞等待
    //         while(IsFull())
    //         {
    //             cout << "生产者进入等待..." << endl;
    //             // 等待时会释放mutex
    //             _pwait_num++;
    //             pthread_cond_wait(&_productor_cond, &_mutex);  // wait的时候，是必定持有锁的；什么时候返回？线程被唤醒&&重新申请并持有锁（会在临界区醒来）
    //             _pwait_num--;
    //             cout << "生产者被唤醒了..." << endl;
    //         }
    //         // isfull不满足或者线程被唤醒了，就能生产 
    //         _q.push(in);
    //         // 走到这里肯定有数据，因为刚push 完，所以如果消费者有wait的线程，就可以唤醒一个
    //         if(_cwait_num)
    //         {
    //             cout << "叫醒消费者..." << endl;
    //             pthread_cond_signal(&_consumer_cond);
    //         }
    //         pthread_mutex_unlock(&_mutex);
    //     }
    //     void Pop(T* out)
    //     {
    //         pthread_mutex_lock(&_mutex);
    //         // 不是你想消费就能消费，必须在队列不为空的情况下才能出队列，否则需要阻塞等待
    //         while(IsEmpty())
    //         {
    //             cout << "消费者进入等待..." << endl;
    //             _cwait_num++;
    //             pthread_cond_wait(&_consumer_cond, &_mutex);
    //             _cwait_num--;
    //             cout << "消费者被唤醒了..." << endl;
    //         }
    //         // isempty不满足或者线程被唤醒了，就能消费 
    //         // 走到这里肯定不为满，因为刚pop完，所以如果生产者有wait的线程，就可以唤醒一个
    //         if(_pwait_num)
    //         {
    //             cout << "叫醒生产者..." << endl;
    //             pthread_cond_signal(&_productor_cond);
    //         }
    //         *out = _q.front();
    //         _q.pop();
    //         pthread_mutex_unlock(&_mutex);
    //     }
    //     ~BlockQueue()
    //     {
    //         pthread_mutex_destroy(&_mutex);
    //         pthread_cond_destroy(&_productor_cond);
    //         pthread_cond_destroy(&_consumer_cond);
    //     }
    // private:
    //     queue<T> _q;  // 保存数据的容器，临界资源
    //     int _cap;  // blockqueue的最大容量
    //     pthread_mutex_t _mutex;  // 互斥锁
    //     pthread_cond_t _productor_cond;  // 生产者条件变量
    //     pthread_cond_t _consumer_cond;  // 消费者条件变量

    //     int _cwait_num;  // 保存生产者wait的数量
    //     int _pwait_num;  // 保存消费者wait的数量
    // };
}